package cn.lix.sc.rpc.client.impl.rpc;

import cn.lix.sc.rpc.client.impl.client.EnhancedRemoteInterface;
import cn.lix.sc.rpc.impl.common.annotations.RpcService;
import cn.lix.sc.rpc.impl.common.dto.EnhancedRpcRequest;
import cn.lix.sc.rpc.impl.common.dto.EnhancedRpcResponse;
import cn.lix.sc.rpc.impl.common.exception.RemoteException;
import cn.lix.sc.rpc.impl.common.utils.JsonUtil;
import cn.lix.sc.rpc.impl.common.utils.MethodParametersJsonConvertor;
import feign.Feign;
import feign.form.FormData;
import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.UUID;

/**
 * 增强的JDK动态代理，支持多种内容类型和文件流处理
 * @author lix
 * @date 2024
 */
public class EnhancedJDKRemoteServiceProxy<T> implements InvocationHandler {

    private final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(this.getClass());

    private String serverId;
    private String beanName;
    private Class<T> targetClazz;
    private int connectTimeout;
    private int readTimeout;
    private RpcService.ContentType[] supportedContentTypes;
    private long maxFileSize;
    
    private Feign.Builder builder;

    public EnhancedJDKRemoteServiceProxy(String serverId, Class<T> targetClazz, String beanName, 
                                        int connectTimeout, int readTimeout, 
                                        RpcService.ContentType[] contentTypes, long maxFileSize) {
        this.serverId = serverId;
        this.beanName = beanName;
        this.targetClazz = targetClazz;
        this.connectTimeout = connectTimeout;
        this.readTimeout = readTimeout;
        this.supportedContentTypes = contentTypes != null ? contentTypes : new RpcService.ContentType[]{RpcService.ContentType.JSON};
        this.maxFileSize = maxFileSize;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("equals".equals(method.getName())) {
            try {
                Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
                return equals(otherHandler);
            } catch (IllegalArgumentException e) {
                return false;
            }
        } else if ("hashCode".equals(method.getName())) {
            return hashCode();
        } else if ("toString".equals(method.getName())) {
            return toString();
        }

        return invokeEnhanced(method, args);
    }

    /**
     * 增强的RPC调用，支持多种内容类型
     */
    private Object invokeEnhanced(Method method, Object[] args) throws Exception {
        // 检测方法参数和返回值类型，确定最佳的调用方式
        RpcService.ContentType contentType = detectContentType(method, args);
        
        switch (contentType) {
            case MULTIPART:
                return invokeFileUpload(method, args);
            case OCTET_STREAM:
                return invokeStreamCall(method, args);
            case TEXT_PLAIN:
                return invokeTextCall(method, args);
            default:
                return invokeJsonCall(method, args);
        }
    }

    /**
     * JSON调用
     */
    private Object invokeJsonCall(Method method, Object[] args) throws Exception {
        EnhancedRemoteInterface target = getBuilder().target(EnhancedRemoteInterface.class, getUrl());
        
        EnhancedRpcRequest request = getEnhancedRequest(method, args, RpcService.ContentType.JSON);
        EnhancedRpcResponse response = target.enhancedDispatch(request);
        
        return handleDirectResponse(response, method.getReturnType());
    }

    /**
     * 文件上传调用
     */
    private Object invokeFileUpload(Method method, Object[] args) throws Exception {
        EnhancedRemoteInterface target = getBuilder().target(EnhancedRemoteInterface.class, getUrl());
        
        // 找到MultipartFile参数
        MultipartFile file = null;
        Object[] otherArgs = new Object[args.length];

        for (int i = 0; i < args.length; i++) {
            if (args[i] instanceof MultipartFile) {
                file = (MultipartFile) args[i];
                otherArgs[i] = null; // 不包含在JSON参数中
            } else {
                otherArgs[i] = args[i];
            }
        }
        
        if (file == null) {
            throw RemoteException.error("未找到MultipartFile参数");
        }
        
        if (file.getSize() > maxFileSize) {
            throw RemoteException.error("文件大小超过限制: {} > {} bytes", file.getSize(), maxFileSize);
        }
        
        // 直接传递MultipartFile给SpringFormEncoder处理
        log.info("准备文件上传: 文件名={}, 类型={}, 大小={} bytes", 
                file.getOriginalFilename(), file.getContentType(), file.getSize());
        
        String parameters = JsonUtil.obj2String(otherArgs);
        String methodKey = MethodParametersJsonConvertor.getMethodKey(targetClazz.getName(), method);
        
        log.info("文件上传调用参数: serviceClass={}, methodKey={}, beanName={}, parameters={}", 
                targetClazz.getName(), methodKey, beanName, parameters);
        
        // 确保参数顺序与服务端接口一致，直接传递MultipartFile
        String finalBeanName = (beanName != null && !beanName.isEmpty()) ? beanName : "";
        EnhancedRpcResponse response = target.uploadDispatch(
            targetClazz.getName(), methodKey, finalBeanName, parameters, file);
        
        return handleDirectResponse(response, method.getReturnType());
    }

    /**
     * 流式数据调用
     */
    private Object invokeStreamCall(Method method, Object[] args) throws Exception {
        EnhancedRemoteInterface target = getBuilder().target(EnhancedRemoteInterface.class, getUrl());
        
        // 检查是否为文件下载调用（返回byte[]且无流式输入参数）
        if (method.getReturnType() == byte[].class && !hasStreamInputParameter(args)) {
            return invokeFileDownload(method, args);
        }
        
        // 找到流式数据参数
        byte[] streamData = null;
        Object[] otherArgs = new Object[args.length];
        
        for (int i = 0; i < args.length; i++) {
            if (args[i] instanceof InputStream) {
                streamData = readInputStream((InputStream) args[i]);
                otherArgs[i] = null;
            } else if (args[i] instanceof byte[]) {
                streamData = (byte[]) args[i];
                otherArgs[i] = null;
            } else {
                otherArgs[i] = args[i];
            }
        }
        
        if (streamData == null) {
            throw RemoteException.error("未找到流式数据参数");
        }
        
        String parameters = JsonUtil.obj2String(otherArgs);
        String methodKey = MethodParametersJsonConvertor.getMethodKey(targetClazz.getName(), method);
        
        ResponseEntity<byte[]> responseEntity = target.streamDispatch(
            targetClazz.getName(), methodKey, beanName, parameters, streamData);
        
        if (responseEntity.getStatusCode().is2xxSuccessful()) {
            byte[] responseData = responseEntity.getBody();
            
            // 根据返回类型转换结果
            if (method.getReturnType() == byte[].class) {
                return responseData;
            } else if (InputStream.class.isAssignableFrom(method.getReturnType())) {
                return new ByteArrayInputStream(responseData);
            } else {
                // 尝试作为字符串处理
                return new String(responseData, "UTF-8");
            }
        }
        
        throw RemoteException.error("流式数据调用失败");
    }
    
    /**
     * 检查参数中是否有流式输入参数
     */
    private boolean hasStreamInputParameter(Object[] args) {
        if (args == null) return false;
        for (Object arg : args) {
            if (arg instanceof InputStream || arg instanceof byte[]) {
                return true;
            }
        }
        return false;
    }

    /**
     * 文件下载调用
     */
    private Object invokeFileDownload(Method method, Object[] args) throws Exception {
        EnhancedRemoteInterface target = getBuilder().target(EnhancedRemoteInterface.class, getUrl());
        
        // 构建EnhancedRpcRequest用于文件下载
        EnhancedRpcRequest request = getEnhancedRequest(method, args, RpcService.ContentType.OCTET_STREAM);
        
        log.info("文件下载调用: serviceClass={}, methodKey={}, args={}", 
                targetClazz.getName(), request.getMethodKey(), JsonUtil.obj2String(args));
        
        // 使用JSON端点但期望返回文件
        EnhancedRpcResponse response = target.enhancedDispatch(request);
        
        if (response != null && response.ok()) {
            // 如果响应包含文件数据，直接返回
            if (response.isFile()) {
                return response.getFileData();
            }
            // 否则尝试从结果字符串转换
            String result = response.getResult();
            if (result != null) {
                // 这里可能需要根据实际情况调整，比如result是base64编码的文件数据
                return result.getBytes("UTF-8");
            }
        }
        
        String errorMsg = response != null ? response.getErrorMsg() : "文件下载失败";
        throw RemoteException.error(errorMsg);
    }

    /**
     * 文本调用
     */
    private Object invokeTextCall(Method method, Object[] args) throws Exception {
        EnhancedRemoteInterface target = getBuilder().target(EnhancedRemoteInterface.class, getUrl());
        
        // 提取文本数据
        String textData = args != null && args.length > 0 ? String.valueOf(args[0]) : "";
        String methodKey = MethodParametersJsonConvertor.getMethodKey(targetClazz.getName(), method);
        
        EnhancedRpcResponse response = target.textDispatch(
            targetClazz.getName(), methodKey, beanName, textData);
        
        return handleDirectResponse(response, method.getReturnType());
    }

    /**
     * 检测内容类型
     */
    private RpcService.ContentType detectContentType(Method method, Object[] args) {
        // 优先检查返回类型 - 如果返回byte[]且没有文件上传参数，则认为是文件下载
        if (method.getReturnType() == byte[].class && !hasFileUploadParameter(args)) {
            return RpcService.ContentType.OCTET_STREAM; // 使用流式处理进行文件下载
        }
        
        if (args == null || args.length == 0) {
            return RpcService.ContentType.JSON;
        }
        
        // 检查是否有文件上传参数
        for (Object arg : args) {
            if (arg instanceof MultipartFile) {
                if (Arrays.asList(supportedContentTypes).contains(RpcService.ContentType.MULTIPART)) {
                    return RpcService.ContentType.MULTIPART;
                }
            }
        }
        
        // 检查是否有流式数据参数
        for (Object arg : args) {
            if (arg instanceof InputStream || arg instanceof byte[]) {
                if (Arrays.asList(supportedContentTypes).contains(RpcService.ContentType.OCTET_STREAM)) {
                    return RpcService.ContentType.OCTET_STREAM;
                }
            }
        }
        
        // 检查是否为纯文本调用
        if (args.length == 1 && args[0] instanceof String && method.getReturnType() == String.class) {
            if (Arrays.asList(supportedContentTypes).contains(RpcService.ContentType.TEXT_PLAIN)) {
                return RpcService.ContentType.TEXT_PLAIN;
            }
        }
        
        return RpcService.ContentType.JSON;
    }
    
    /**
     * 检查参数中是否有文件上传参数
     */
    private boolean hasFileUploadParameter(Object[] args) {
        if (args == null) return false;
        for (Object arg : args) {
            if (arg instanceof MultipartFile) {
                return true;
            }
        }
        return false;
    }

    /**
     * 处理增强响应
     */
    private Object handleEnhancedResponse(ResponseEntity<EnhancedRpcResponse> responseEntity, Class<?> returnType) throws Exception {
        if (!responseEntity.getStatusCode().is2xxSuccessful()) {
            throw RemoteException.error("HTTP调用失败: " + responseEntity.getStatusCode());
        }
        
        EnhancedRpcResponse response = responseEntity.getBody();
        if (response == null) {
            throw RemoteException.error("响应体为空");
        }
        
        if (!response.ok()) {
            throw RemoteException.error(response.getErrorMsg());
        }
        
        return convertResult(response.getResult(), returnType);
    }

    /**
     * 处理直接响应（无ResponseEntity包装）
     */
    private Object handleDirectResponse(EnhancedRpcResponse response, Class<?> returnType) throws Exception {
        if (response == null) {
            throw RemoteException.error("响应体为空");
        }
        
        if (!response.ok()) {
            throw RemoteException.error(response.getErrorMsg());
        }
        
        return convertResult(response.getResult(), returnType);
    }

    /**
     * 转换结果
     */
    private Object convertResult(String result, Class<?> returnType) {
        if (returnType == void.class || returnType == Void.class) {
            return null;
        }
        
        if (returnType == String.class) {
            return result;
        }
        
        return JsonUtil.string2Obj(result, returnType);
    }

    /**
     * 读取InputStream
     */
    private byte[] readInputStream(InputStream inputStream) throws IOException {
        try (java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream()) {
            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                baos.write(buffer, 0, bytesRead);
            }
            return baos.toByteArray();
        }
    }

    /**
     * 创建增强请求对象
     */
    private EnhancedRpcRequest getEnhancedRequest(Method method, Object[] args, RpcService.ContentType contentType) {
        EnhancedRpcRequest request = new EnhancedRpcRequest();
        request.setServiceClass(targetClazz.getName());
        request.setBeanName(beanName);
        request.setMethodKey(MethodParametersJsonConvertor.getMethodKey(targetClazz.getName(), method));
        request.setParameters(JsonUtil.obj2String(args));
        request.setContentType(contentType);
        request.setRequestId(UUID.randomUUID().toString().replace("-", ""));
        return request;
    }

    private String getUrl() {
        StringBuilder stringBuilder = new StringBuilder(128);
        if (!this.serverId.startsWith("http")) {
            stringBuilder.append("http://").append(this.serverId);
        } else {
            stringBuilder.append(this.serverId);
        }
        return stringBuilder.toString();
    }

    private Feign.Builder getBuilder() {
        if (builder == null) {
            builder = EnhancedFeignBuilder.createEnhancedFeignBuilder(connectTimeout, readTimeout);
        }
        return builder;
    }
} 